/*
    SUSAN® - Sole of Unix Save ANything

   Copyright (C) 2004-2010 Hechzh Property
   Copyright (C) 2016-2019 Hechzh Property

   
*/
/*
 * Kern Sibbald, MMIV and MMVII
 */
/**
 * @file
 * Doubly linked list  -- dlist
 *
 * See the end of the file for the dlistString class which
 * facilitates storing strings in a dlist.
 */

#ifndef BAREOS_LIB_DLIST_H_
#define BAREOS_LIB_DLIST_H_

#include "include/bareos.h"
#include "lib/dlink.h"
#include "lib/message.h"

#define M_ABORT 1

/* In case you want to specifically specify the offset to the link */
#define OFFSET(item, link) (int)((char*)(link) - (char*)(item))
/**
 * There is a lot of extra casting here to work around the fact
 * that some compilers (Sun and Visual C++) do not accept
 * (void *) as an lvalue on the left side of an equal.
 *
 * Loop var through each member of list
 */
#ifdef HAVE_TYPEOF
#define foreach_dlist(var, list)                                         \
  for ((var) = nullptr; (list ? ((var) = (typeof(var))(list)->next(var)) \
                              : nullptr) != nullptr;)
#else
#define foreach_dlist(var, list)                                              \
  for ((var) = nullptr;                                                       \
       (list ? (*((void**)&(var)) = (void*)((list)->next(var))) : nullptr) != \
       nullptr;)
#endif

class dlist {
  void* head;
  void* tail;
  int16_t loffset;
  uint32_t num_items;

 public:
  dlist(void* item, dlink* link);
  dlist(void);
  ~dlist() { destroy(); }
  void init(void* item, dlink* link);
  void init();
  void prepend(void* item);
  void append(void* item);
  void SetPrev(void* item, void* prev);
  void SetNext(void* item, void* next);
  void* get_prev(void* item);
  void* get_next(void* item);
  dlink* get_link(void* item);
  void InsertBefore(void* item, void* where);
  void InsertAfter(void* item, void* where);
  void* binary_insert(void* item, int compare(void* item1, void* item2));
  void* binary_search(void* item, int compare(void* item1, void* item2));
  void BinaryInsertMultiple(void* item, int compare(void* item1, void* item2));
  void remove(void* item);
  bool empty() const;
  int size() const;
  void* next(void* item);
  void* prev(void* item);
  void destroy();
  void* first() const;
  void* last() const;
};

inline void dlist::init()
{
  head = tail = nullptr;
  loffset = 0;
  num_items = 0;
}


/**
 * Constructor called with the address of a
 *   member of the list (not the list head), and
 *   the address of the link within that member.
 * If the link is at the beginning of the list member,
 *   then there is no need to specify the link address
 *   since the offset is zero.
 */
inline dlist::dlist(void* item, dlink* link) { init(item, link); }

/* Constructor with link at head of item */
inline dlist::dlist(void)
    : head(nullptr), tail(nullptr), loffset(0), num_items(0)
{
  return;
}

inline void dlist::SetPrev(void* item, void* prev)
{
  ((dlink*)(((char*)item) + loffset))->prev = prev;
}

inline void dlist::SetNext(void* item, void* next)
{
  ((dlink*)(((char*)item) + loffset))->next = next;
}

inline void* dlist::get_prev(void* item)
{
  return ((dlink*)(((char*)item) + loffset))->prev;
}

inline void* dlist::get_next(void* item)
{
  return ((dlink*)(((char*)item) + loffset))->next;
}


inline dlink* dlist::get_link(void* item)
{
  return (dlink*)(((char*)item) + loffset);
}


inline bool dlist::empty() const { return head == nullptr; }

inline int dlist::size() const { return num_items; }


inline void* dlist::first() const { return head; }

inline void* dlist::last() const { return tail; }

/**
 * C string helper routines for dlist
 *   The string (char *) is kept in the node
 *
 *   Kern Sibbald, February 2007
 *
 */
class dlistString {
 public:
  char* c_str() { return str_; }

 private:
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-private-field"
#endif
  dlink link_;
#ifdef __clang__
#pragma clang diagnostic pop
#endif
  char str_[1];
  /* !!! Don't put anything after this as this space is used
   *     to hold the string in inline
   */
};

extern dlistString* new_dlistString(const char* str, int len);
extern dlistString* new_dlistString(const char* str);

#endif /* BAREOS_LIB_DLIST_H_ */
